/* autor Konjkov.VV@gmail.com */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <iconv.h>

#include "data.h"
#include "common.h"
#include "packetinfo.h"
#include "nodesconf.h"

int data_prepare(char *node) {
SQLHSTMT stmt;
SQLRETURN retcode;

char   buffer[MAX_STATEMENT_LEN];
char   owner[MAX_NAME_LEN];
char   table[MAX_NAME_LEN];
int    prevpacket;
int    curpacket = 5;

SQLSMALLINT   numcols;
int       col;
char      **value;
SQLLEN    *size;
SQLLEN    displen;
FILE      *table_dat, *table_del, *packet_info;

struct nodesconf	conf;
struct group*		nextgroup;
struct nodesection*	nextnode;
struct nodename*	nextnodename;
struct nodetable*	nexttable;

    packet_info = fopen("./packet.info","w");

    prepared_get(node, &prevpacket);
    packetbegin(packet_info, node, prevpacket, curpacket);
    nodesconf(&conf);
    nextnode=conf.nodessection;
    while(nextnode!=NULL) {
        nextnodename=nextnode->nodes;
        nexttable=nextnode->tables;
        while(nextnodename!=NULL) {
            printf("%s\n",nextnodename->name);
            if (strcmp(nextnodename->name, node)==0) {
                while(nexttable!=NULL) {
                    printf("%s %s %s %s\n", nexttable->name, nexttable->fields, nexttable->condi, nexttable->alias);
                    sscanf(nexttable->name, "%[^.].%s", owner, table);

                    tablesection_pkeyfields(packet_info, owner, table);
                    tablesection_otherfields(packet_info, owner, table);

// Выгрузка данных из основной таблицы
                    sprintf( buffer, "SELECT %s FROM %s.%s WHERE ("SQL_SYSFIELD1" = 0 OR "SQL_SYSFIELD1" > %d) %s", nexttable->fields, owner, table, prevpacket, nexttable->condi);
                    SQLAllocStmt( dbc, &stmt );
// Фетчить по одной строке
                    EXEC_SQL( SQLSetStmtAttr( stmt, SQL_ATTR_ROW_ARRAY_SIZE, (SQLPOINTER) 1, 0) );
                    printf("%s\n", buffer);
                    EXEC_SQL( SQLExecDirect( stmt, buffer, SQL_NTS ) );
                    EXEC_SQL( SQLNumResultCols( stmt, &numcols) );

                    tablesection_createclause(packet_info, stmt);

                    value = malloc(numcols * sizeof(char*));
                    size = malloc(numcols * sizeof(SQLLEN));
                    for (col = 0; col < numcols; col++) {
                        EXEC_SQL( SQLColAttributes( stmt, col+1, SQL_COLUMN_DISPLAY_SIZE, NULL, 0 , NULL, &displen) );
                        value[col] = malloc((displen + 1) * sizeof(char));
                        EXEC_SQL( SQLBindCol( stmt, col+1, SQL_C_CHAR, value[col], displen+1, &size[col]) );
                    }
                    sprintf( buffer, "./%s_%s.dat", owner, table);
                    table_dat = fopen(buffer, "w");
                    while(1) {
                        retcode = SQLFetch(stmt);
                        if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) {
                            for (col = 0; col < numcols; col++) {
                                if (size[col] == SQL_NULL_DATA) {
                                    fprintf(table_dat,",");
                                } else {
                                    fprintf(table_dat,"%s,", value[col]);
                                }
                            }
                            fprintf(table_dat,"\n");
                        } else if (retcode == SQL_NO_DATA) {
//                            for (col = 0; col < numcols; col++) {
//                                free(value[col]);
//                                free(size[col]);
//                            }
//                            free(value);
//                            free(size);
                            fclose(table_dat);
                            break;
                        } else {
                            fclose(table_dat);
                            return syncctl_error( stmt , __FILE__, __LINE__);
                        }
                    }
                    SQLFreeStmt( stmt, SQL_CLOSE );
// Обновление поля packet таблицы
                    sprintf( buffer, "UPDATE %s.%s SET "SQL_SYSFIELD1" = %i WHERE "SQL_SYSFIELD1" = 0 %s", owner, table, curpacket+1, nexttable->condi);
                    SQLAllocStmt( dbc, &stmt );
                    EXEC_SQL( SQLExecDirect( stmt, buffer, SQL_NTS ) );
                    SQLFreeStmt( stmt, SQL_CLOSE );
// Выгрузка данных из таблицы _del
                    sprintf( buffer, "SELECT %s FROM %s.%s_"SQL_PREFIX_DELTABLE" WHERE ("SQL_SYSFIELD1" = 0 OR "SQL_SYSFIELD1" > %i) AND "SQL_SYSFIELD2" <= %i %s", nexttable->fields, owner, table, prevpacket, prevpacket, nexttable->condi);
                    SQLAllocStmt( dbc, &stmt );
// Фетчить по одной строке
                    EXEC_SQL( SQLSetStmtAttr( stmt, SQL_ATTR_ROW_ARRAY_SIZE, (SQLPOINTER) 1, 0) );
                    EXEC_SQL( SQLExecDirect( stmt, buffer, SQL_NTS ) );
                    EXEC_SQL( SQLNumResultCols( stmt, &numcols) );

                    sprintf( buffer, "./%s_%s.del", owner, table);
                    table_del = fopen(buffer, "w");
                    fclose(table_del);
                    SQLFreeStmt( stmt, SQL_CLOSE );
// Обновление поля packet таблицы _del
                    sprintf( buffer, "UPDATE %s.%s_"SQL_PREFIX_DELTABLE" SET "SQL_SYSFIELD1" = %i WHERE "SQL_SYSFIELD1" = 0 %s", owner, table, curpacket+1, nexttable->condi);
                    SQLAllocStmt( dbc, &stmt );
                    EXEC_SQL( SQLExecDirect( stmt, buffer, SQL_NTS ) );
                    SQLFreeStmt( stmt, SQL_CLOSE );
// конец цикла
                    nexttable=nexttable->next;
                }
            }
            nextnodename=nextnodename->next;
        }
        nextnode=nextnode->next;
    }

    prepared_set(node, curpacket+1);

// SQL-скрипт для формирования пакета - КОНЕЦ
// Если были выгруженны хоть какие-либо данные
#define SQL_DATA_PREPARE_AFTER "UPDATE "SQL_SCHEMA".syncMAIN SET current_packet = ?"
//    sprintf( buffer, SQL_DATA_PREPARE_AFTER, schema);
    SQLAllocStmt( dbc, &stmt );
    EXEC_SQL( SQLBindParameter( stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, 50, 0, curpacket+1, 0, SQL_TYPE_NULL) );
    EXEC_SQL( SQLExecDirect( stmt, buffer, SQL_NTS ) );
    SQLFreeStmt( stmt, SQL_CLOSE );

    packetend(packet_info);

// SQL-скрипт для формирования пакета - ДЛЯ ОТДЕЛЬНОГО ПРЕДСТАВЛЕНИЯ
// Параметры шаблона как для таблицы, плюс:
//   select_special  - названия всех служебных полей в виде списка:
//                       pkt_<field1> as ${SQL_FIELDPREFIX1}pkt_<field1>
//                       pkt_<field2> as ${SQL_FIELDPREFIX1}pkt_<field2>
//                       ...
//                       pkt_<fieldN> as ${SQL_FIELDPREFIX1}pkt_<fieldN>
//   changed_special - условие на изменение хотя бы одного из служебного
//                     поля (на основе шаблона SQL_DATA_PREPARE_VIEW_IF)
//   update_special  - обновление всех служебных полей (на основе шаблона
//                     SQL_DATA_PREPARE_VIEW_UPDATE)

#define SQL_DATA_PREPARE_VIEW "SELECT <select_fields> FROM <owner>.<table> WHERE ( <changed_special> ) <filter>"
//  <update_special>

// SQL-скрипт для формирования пакета - ДЛЯ ОТДЕЛЬНОГО ПРЕДСТАВЛЕНИЯ
// Условие на изменение отдельного служебного поля

#define SQL_DATA_PREPARE_VIEW_IF "<special_field> = 0 or <special_field> > (SELECT prepared FROM <schema>.syncNODES WHERE name = '<node>')"

// SQL-скрипт для формирования пакета - ДЛЯ ОТДЕЛЬНОГО ПРЕДСТАВЛЕНИЯ
// Для изменения отдельного служебного поля

#define SQL_DATA_PREPARE_VIEW_UPDATE "UPDATE <owner>.<table> SET <special_field> = %s WHERE <special_field> = 0"

    return 0;
}

int data_update(char *node) {
// SQL-скрипт для обновления данных - ДЛЯ ОТДЕЛЬНОЙ ТАБЛИЦЫ
// Параметры шаблона:
//   owner         - владелец таблицы
//   table         - имя таблицы
//   file_data     - полный путь и имя файла с данными таблицы с удаленной БД
//   file_del      - полный путь и имя файла со стертыми записями из таблицы
//                   с удаленной БД
//   fields_def    - определения всех полей таблицы (кроме служебных полей)
//                   из удаленной БД, перечисленные через запятую
//   select_fields - названия полей (кроме служебных), которые
//                   одновременно присутствуют в таблице в локальной и
//                   удаленной БД, в виде:
//                     $<field1> as $SQL_FIELDPREFIX1<field1>,
//                     $<field2> as $SQL_FIELDPREFIX1<field2>,
//                     ...
//                     $<fieldN> as $SQL_FIELDPREFIX1<fieldN>,
//   insert_fields - названия полей из common_fields в виде:
//                     $SQL_FIELDPREFIX2<field1>,
//                     $SQL_FIELDPREFIX2<field2>,
//                     ...
//                     $SQL_FIELDPREFIX2<fieldN>
//   insert_values - названия полей из insert_fields в виде:
//                     $SQL_FIELDPREFIX2<field1>,
//                     $SQL_FIELDPREFIX2<field2>,
//                     ...
//                     $SQL_FIELDPREFIX2<fieldN>
//   pkey_fields   - поля, входящие в первичный ключ таблицы в локальной
//                   БД в виде:
//                     <field1> = $SQL_FIELDPREFIX2<field1> and
//                     <field2> = $SQL_FIELDPREFIX2<field2> and
//                     ...
//                     <fieldN> = $SQL_FIELDPREFIX2<fieldN>
//   update_fields - поля из удаленной таблицы, значения по которым
//                   обновляются в локальной таблице (по другому -
//                   поля из common_fields за минусом pkey_fields)
//                   в виде:
//                     <field1> = $SQL_FIELDPREFIX2<field1>,
//                     <field2> = $SQL_FIELDPREFIX2<field2>,
//                     ...
//                     <fieldN> = $SQL_FIELDPREFIX2<fieldN>

#define SQL_DATA_UPDATE \
" CREATE TEMP TABLE <table>_${SQL_PREFIX_TEMPTABLE}\
  (<fields_def>);\
\
  COPY <table>_${SQL_PREFIX_TEMPTABLE} from '<file_del>' CSV QUOTE AS '''';\
\
CREATE OR REPLACE FUNCTION <schema>.ftemp()\
 RETURNS integer AS\
\$BODY\$\
DECLARE\
  ${SQL_CURSORNAME} RECORD;\
BEGIN\
\
   FOR ${SQL_CURSORNAME} IN SELECT <select_fields> FROM <table>_${SQL_PREFIX_TEMPTABLE} LOOP\
\
      DELETE FROM <owner>.<table> WHERE <pkey_fields>;\
\
   END LOOP;\
\
   return 1;\
END;\
\$BODY\$\
LANGUAGE 'plpgsql'; \
\
SELECT <schema>.ftemp();\
\
DROP FUNCTION <schema>.ftemp();\
\
\
  TRUNCATE <table>_${SQL_PREFIX_TEMPTABLE};\
\
  COPY <table>_${SQL_PREFIX_TEMPTABLE} from '<file_data>' CSV QUOTE AS '''';\
\
\
CREATE OR REPLACE FUNCTION <schema>.ftemp()\
 RETURNS integer AS\
\$BODY\$\
DECLARE\
  ${SQL_CURSORNAME} RECORD;\
BEGIN\
\
   FOR ${SQL_CURSORNAME} IN SELECT <select_fields> FROM <table>_${SQL_PREFIX_TEMPTABLE} LOOP\
\
      if exists(SELECT * FROM <owner>.<table> WHERE <pkey_fields>)\
         then UPDATE <owner>.<table> SET <update_fields> WHERE <pkey_fields>;\
         else INSERT INTO <owner>.<table>(<insert_fields>) VALUES(<insert_values>);\
      end if;\
\
   END LOOP;\
\
   return 1;\
END;\
\$BODY\$\
LANGUAGE 'plpgsql'; \
\
SELECT <schema>.ftemp();\
\
DROP FUNCTION <schema>.ftemp();\
\
\
DROP TABLE <table>_${SQL_PREFIX_TEMPTABLE};"
SQLHSTMT stmt, stmt1;
SQLRETURN retcode, retcode1;

struct packetinfo info;
struct table*  nexttable;
struct clause* nextclause;

char   owner[MAX_NAME_LEN];
char   table[MAX_NAME_LEN];

char*  token;

char   buffer[MAX_STATEMENT_LEN];
char   field[MAX_STATEMENT_LEN];
char   pkeyfields[MAX_NAME_LEN*64];
char   updatefields[MAX_NAME_LEN*64] = "AZSNO=?,";
char   insertfields[MAX_NAME_LEN*64];
char   insertvalues[MAX_NAME_LEN*64];

struct field primarykey;
struct field otherfield;
struct field* nextfield;

char   value[6][MAX_STATEMENT_LEN];
int    val = 0;
int    param = 0;
SQLLEN    parSize;

SQLCHAR       Catalog[MAX_NAME_LEN], Schema[MAX_NAME_LEN], TableName[MAX_NAME_LEN], ColumnName[MAX_NAME_LEN];
SQLLEN        ColumnNameSize;

int i=0, j=0, k=0, l=0;

FILE   *table_dat, *table_del;

int    curpacket = 5;

    packetinfo(&info);

    nexttable = info.table;
    while (nexttable != NULL) {
        sscanf(nexttable->name, "%[^.].%s", owner, table);
// обработка файла .del
        sprintf(buffer, "./%s_%s.del", owner, table);
        table_del = fopen(buffer, "r");
        if (table_del) {
            SQLAllocStmt( dbc, &stmt );
// Поля первичного ключа в виде fieldname1=? AND fieldname2=?,...
            param = 0;
            tableprimarykeys(owner, table, &primarykey);
            nextfield = &primarykey;
            while( nextfield != NULL ) {
                    if ( strcmp( nextfield->name, "packet") && strcmp( nextfield->name, "initpacket") ) {
// Для каждого поля входящего в певичный ключ, найти поле в описании таблицы в packet.info и связать командой SQLBindParameter по порядковому номеру в packet.info
                        nextclause = nexttable->clause;
                        val = 0;
                        while (nextclause != NULL) {
// Убрать кавычки из nextclause->name и привести к нижнему регистру
                            if ( !strcmp(nextclause->name, nextfield->name) ) {
                                param++;
                                EXEC_SQL( SQLBindParameter( stmt, param, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, MAX_STATEMENT_LEN, 0, value[val], 0, &parSize) );
                                printf("%i %i %s\n", param, val, nextclause->name);
                                break;
                            }
                            nextclause = nextclause->next;
                            val++;
                        }
                        i += sprintf(pkeyfields + i, "%s=? AND ", nextfield->name);
                    }
            nextfield = nextfield->next;
            }
            pkeyfields[i-5] = '\0';
            printf("%s\n", pkeyfields);

            sprintf( buffer,"DELETE FROM %s.%s WHERE %s", owner, table, pkeyfields);
            printf("buffer %s\n", buffer);
            EXEC_SQL( SQLPrepare( stmt, buffer, SQL_NTS ) );
// Чтение из файла
            while (!feof(table_del)) {
                fgets(buffer, MAX_STATEMENT_LEN, table_del);
                val = 0;
// Здесь изменить кодировку
                token = buffer-1;
                while (token != NULL) {
                    if (sscanf(token+1, "%[^,]", value[val])) {
                        printf("field[%i] = %s\n", val, value[val]);
                    } else {
                        printf("Error Primarykey is NULL\n");
                    }
                    token = strchr(token+1, ',');
                    val++;
                }
                EXEC_SQL( SQLExecute(stmt) );
            }
            SQLFreeStmt( stmt, SQL_CLOSE );
            fclose(table_del);
        } else {
            printf("error open ./%s_%s.dat", owner, table);
        }
// обработка файла .dat
        sprintf(buffer, "./%s_%s.dat", owner, table);
        table_dat = fopen(buffer, "r");
        if (table_dat) {
            SQLAllocStmt( dbc, &stmt );
            param = 0;
// Для каждого поля входящего в певичный ключ, найти поле в описании таблицы в packet.info и связать командой SQLBindParameter по порядковому номеру в packet.info
            tableotherfields(owner, table, &otherfield);
            nextfield = &otherfield;
// Из всех неключевых полей таблицы выбрать поля из описании таблицы в packet.info, связать командой SQLBindParameter по порядковому номеру в packet.info
            while( nextfield != NULL ) {
                    if ( strcmp( nextfield->name, "packet") && strcmp( nextfield->name, "initpacket") ) {
                        nextclause = nexttable->clause;
                        val = 0;
                        while (nextclause != NULL) {
// Убрать кавычки из nextclause->name и привести к нижнему регистру
                            if ( !strcmp(nextclause->name, nextfield->name) ) {
                                param++;
                                EXEC_SQL( SQLBindParameter( stmt, param, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, MAX_STATEMENT_LEN, 0, value[val], 0, &parSize) );
                                printf("%i %i %s\n", param, val, nextclause->name);
                                break;
                            }
                            nextclause = nextclause->next;
                            val++;
                        }
                        j += sprintf(updatefields + j, "%s=? AND ", nextfield->name);
                    }
            nextfield = nextfield->next;
            }
            updatefields[j-1] = '\0';
// Снова для каждого поля входящего в певичный ключ, найти поле в описании таблицы в packet.info и связать командой SQLBindParameter по порядковому номеру в packet.info
            ;
// Из всех полей таблицы выбрать поля из описании таблицы в packet.info, связать командой SQLBindParameter по порядковому номеру в packet.info
// вставка записи insertfields, insertvalues
            nextclause = nexttable->clause;
            while (nextclause != NULL) {
                k += sprintf(insertfields + k, "%s,", nextclause->name);
                l += sprintf(insertvalues + l, "?,");
                nextclause = nextclause->next;
            }
            insertfields[k-1] = '\0';
            insertvalues[l-1] = '\0';
            printf("%s\n %s\n %s\n", pkeyfields, insertfields, insertvalues);

            sprintf( buffer, "if exists(SELECT * FROM %s.%s WHERE %s) then UPDATE %s.%s SET %s WHERE %s; else INSERT INTO %s.%s (%s) VALUES(%s); end if"\
            , owner, table, pkeyfields, owner, table, updatefields, pkeyfields, owner, table, insertfields, insertvalues);
            printf("%s\n", buffer);
            SQLAllocStmt( dbc, &stmt );
            EXEC_SQL( SQLPrepare( stmt, buffer, SQL_NTS ) );
//            EXEC_SQL( SQLExecute( stmt) );
            SQLFreeStmt( stmt, SQL_CLOSE );
            fclose(table_dat);
        } else {
            printf("error open ./%s_%s.dat", owner, table);
        }
        nexttable = nexttable->next;
    }
    updated_set(node, curpacket+1);
    return 0;
}
